/**
 * 
 * Copyright (C) 2015 by Elcom Software Company
 *
 * @id: Json.java, Mar 19, 2015
 *
 */
package com.nip.commons.json.abs;

import java.lang.reflect.Field;

import com.nip.commons.json.annotations.JSONField;
import com.nip.commons.json.annotations.JSONObject;
import com.nip.commons.json.commons.JSONCommon;
import com.nip.commons.json.exception.JSONException;

/**
 * @author ThangNM<thangnm@elcom.com.vn>
 * 
 * @version v
 */
public class Json {

    public String toJson() throws JSONException {
        JSONObject jsonObject = this.getClass().getAnnotation(JSONObject.class);
        if (jsonObject == null) {
            throw new JSONException("Object not define @JSONObject(name=?)");
        }

        JSONField jField;
        StringBuilder sb = new StringBuilder("{");
        for (Field field : this.getClass().getDeclaredFields()) {
            field.setAccessible(true);
            jField = field.getAnnotation(JSONField.class);

            if (jField == null) {
                continue;
            }

            sb.append(toJsonValue(field, jField) + ",");
        }

        sb.append("}");
        return sb.toString().trim().replace(",}", "}").replace(",]", "]");
    }

    /**
     * @param field
     * @param jField
     * @return
     * @throws JSONException
     */
    private String toJsonValue(Field field, JSONField jField)
            throws JSONException {

        String key = "\"" + jField.name() + "\":";

        switch (jField.type()) {
        case PRIMITIVE:

            return key + getPrimitiveValue(field);

        case JSON_OBJECT:

            try {
                return key + ((Json) field.get(this)).toJson();
            } catch (NullPointerException e) {
                return "\"null\"";
            } catch (Exception e) {
                throw new JSONException(e);
            }

        case ARRAY:

            return key + getArrayPrimitiveValue(field);

        case JSON_ARRAY:

            return key + getArrayJsonObject(field);

        default:
            throw new JSONException("Unsuport Json field type " + jField.type());
        }
    }

    /**
     * @param field
     * @return
     * @throws JSONException
     */
    private String getArrayJsonObject(Field field) throws JSONException {
        StringBuilder sb = new StringBuilder("[");
        try {
            Object[] objecs = (Object[]) field.get(this);
            if (objecs != null && objecs.length > 0) {

                Json t = null;
                for (Object t1 : objecs) {
                    if (t1 != null) {
                        t = (Json) t1;
                        break;
                    }
                }

                String key = JSONCommon.ELEMENT_DEFAULT;

                if (t != null) {
                    JSONObject jsonObject = t.getClass().getAnnotation(
                            JSONObject.class);
                    if (jsonObject == null) {
                        throw new JSONException("Object "
                                + t.getClass().getCanonicalName()
                                + " not define @JSONObject(name=?)");
                    }
                    key = jsonObject.name();
                }

                for (Object o : objecs) {
                    sb.append("{\"" + key + "\":");
                    if (o == null) {
                        sb.append("\"null\"},");
                    } else {
                        sb.append(((Json) o).toJson() + "},");
                    }
                }
            }
        } catch (NullPointerException e) {

        } catch (Exception e) {
            throw new JSONException(e);
        }
        sb.append("]");
        return sb.toString().replace(",]", "]");
    }

    /**
     * @param field
     * @return
     * @throws JSONException
     */
    private String getArrayPrimitiveValue(Field field) throws JSONException {

        StringBuilder sb = new StringBuilder("[");
        try {
            Object[] objecs = (Object[]) field.get(this);
            if (objecs != null) {
                for (Object o : objecs) {
                    sb.append("{\"" + JSONCommon.ELEMENT_DEFAULT + "\":");
                    if (o.getClass().getName().equals(String.class.getName())) {
                        sb.append("\"" + o + "\"},");
                    } else {
                        sb.append(o + "},");
                    }
                }
            }
        } catch (NullPointerException e) {

        } catch (Exception e) {
            throw new JSONException(e);
        }
        sb.append("]");
        return sb.toString().replace(",]", "]");
    }

    /**
     * @param field
     * @return
     */
    private String getPrimitiveValue(Field field) {
        // TODO Auto-generated method stub
        try {
            Object value = field.get(this);

            if (value.getClass().getName().equals(String.class.getName())) {
                return "\"" + value + "\"";
            }
            return value.toString();

        } catch (NullPointerException e) {
            return "\"null\"";
        } catch (Exception e) {
            return "\"" + e.toString() + "\"";
        }
    }

}
